package besta.moon.engine.selector.multioper;

import besta.moon.MoonException;
import besta.moon.MoonConnection;
import besta.moon.engine.View;
import besta.moon.engine.selector.DataSource;
import besta.moon.sql.datatype.MutableNumeric;
import besta.moon.MoonCode;
import besta.moon.engine.selector.DataSources;

public final class UnionAll extends DataSource {

    private final DataSources dataSources = new DataSources();
    private int dataSourceIdx;
    private DataSource currentDS;
    private int row;

    public void addDataSource(DataSource ds) {
        dataSources.add(ds);
        currentDS = dataSources.get(0);
    }

    @Override
    public boolean init(MoonConnection con) throws Exception {
        boolean result = false;
        int colCount = -1;
        for (int i = 0; i < dataSources.size(); i++) {
            DataSource ds = dataSources.get(i);
            result |= ds.init(con);
            int nextColCount = ds.getTableView().columns.size();
            if (colCount == -1) {
                colCount = nextColCount;
            } else if (colCount != nextColCount) {
                throw MoonException.create(MoonCode.UNION_DIFFERENT_COLS, new Object[]{colCount, nextColCount});
            }
        }
        return result;
    }

    @Override
    public final boolean isNull(int colIdx) throws Exception {
        return currentDS.isNull(colIdx);
    }

    @Override
    public final boolean getBoolean(int colIdx) throws Exception {
        return currentDS.getBoolean(colIdx);
    }

    @Override
    public final int getInt(int colIdx) throws Exception {
        return currentDS.getInt(colIdx);
    }

    @Override
    public final long getLong(int colIdx) throws Exception {
        return currentDS.getLong(colIdx);
    }

    @Override
    public final float getFloat(int colIdx) throws Exception {
        return currentDS.getFloat(colIdx);
    }

    @Override
    public final double getDouble(int colIdx) throws Exception {
        return currentDS.getDouble(colIdx);
    }

    @Override
    public final long getMoney(int colIdx) throws Exception {
        return currentDS.getMoney(colIdx);
    }

    @Override
    public final MutableNumeric getNumeric(int colIdx) throws Exception {
        return currentDS.getNumeric(colIdx);
    }

    @Override
    public final Object getObject(int colIdx) throws Exception {
        return currentDS.getObject(colIdx);
    }

    @Override
    public final String getString(int colIdx) throws Exception {
        return currentDS.getString(colIdx);
    }

    @Override
    public final byte[] getBytes(int colIdx) throws Exception {
        return currentDS.getBytes(colIdx);
    }

    @Override
    public final int getDataType(int colIdx) {
        return currentDS.getDataType(colIdx);
    }

    @Override
    public View getTableView() {
        return currentDS.getTableView();
    }

    @Override
    public final boolean isScrollable() {
        return false;
    }

    @Override
    public final void beforeFirst() throws Exception {
        dataSourceIdx = 0;
        currentDS = dataSources.get(0);
        currentDS.beforeFirst();
        row = 0;
    }

    public final boolean first() throws Exception {
        dataSourceIdx = 0;
        currentDS = dataSources.get(0);
        boolean b = currentDS.first();
        row = b ? 1 : 0;
        return b;
    }

    public final boolean next() throws Exception {
        boolean n = currentDS.next();
        row++;
        if (n) {
            return true;
        }
        while (dataSources.size() > dataSourceIdx + 1) {
            currentDS = dataSources.get(++dataSourceIdx);
            currentDS.beforeFirst();
            n = currentDS.next();
            if (n) {
                return true;
            }
        }
        row = 0;
        return false;
    }

    @Override
    public final void afterLast() throws Exception {
        dataSourceIdx = dataSources.size() - 1;
        currentDS = dataSources.get(dataSourceIdx);
        currentDS.afterLast();
        row = 0;
    }

    @Override
    public final int getRow() throws Exception {
        return row;
    }

    private final int getBitCount() {
        int size = dataSources.size();
        int bitCount = 0;
        while (size > 0) {
            bitCount++;
            size >>= 1;
        }
        return bitCount;
    }

    @Override
    public final long getRowPosition() {
        int bitCount = getBitCount();
        return dataSourceIdx | currentDS.getRowPosition() << bitCount;
    }

    @Override
    public final void setRowPosition(long rowPosition) throws Exception {
        int bitCount = getBitCount();
        int mask = 0xFFFFFFFF >>> (32 - bitCount);
        dataSourceIdx = (int) rowPosition & mask;
        currentDS = dataSources.get(dataSourceIdx);
        currentDS.setRowPosition(rowPosition >> bitCount);
    }

    @Override
    public final boolean rowInserted() {
        return currentDS.rowInserted();
    }

    @Override
    public final boolean rowDeleted() {
        return currentDS.rowDeleted();
    }

    @Override
    public final void nullRow() {
        currentDS.nullRow();
        row = 0;
    }

    @Override
    public final void noRow() {
        currentDS.noRow();
        row = 0;
    }

    @Override
    public final void execute() throws Exception {
        for (int i = 0; i < dataSources.size(); i++) {
            dataSources.get(i).execute();
        }
    }

}
